package br.ufpe.abaco.ParserGen;

import br.ufpe.abaco.ParserGen.lexer.LexerFacade;
import br.ufpe.abaco.ParserGen.lexer.RegExp;
import br.ufpe.abaco.ParserGen.lexer.Token;
import br.ufpe.abaco.ParserGen.parser.BNFGrammar;
import br.ufpe.abaco.ParserGen.parser.GrammarSymbol;
import br.ufpe.abaco.ParserGen.parser.NonTerminalSymbol;
import br.ufpe.abaco.ParserGen.parser.Parser;
import br.ufpe.abaco.ParserGen.parser.Production;
import br.ufpe.abaco.ParserGen.parser.SemanticAction;
import br.ufpe.abaco.ParserGen.parser.TerminalSymbol;
import br.ufpe.abaco.ParserGen.parser.TokenAction;

public class RegExpCompiler {
	/*
	  RegExp --> SeqExp 
	           | SeqExp "|" RegExp.
	
	  SeqExp --> SingleExp 
	           | SingleExp SeqExp.
	  SingleExp --> char | "-" |
	              | "[" "-" Ranges "]" 
	              | "[" Ranges "]" 
	              | "(" RegExp ")"  
	              | SingleExp "+" 
	              | SingleExp "*" 
	              | SingleExp "?".
	  Ranges --> char
	           | char "-" char
	           | Ranges Ranges
	
	 */

	LexerFacade aLexico = new LexerFacade();
	BNFGrammar aSintatico = new BNFGrammar();

	void addProduction(
		SemanticAction ac,
		NonTerminalSymbol left,
		GrammarSymbol right[]) {
		Production p = aSintatico.addProduction(left, right);
		if (ac != null)
			p.setSemanticAction(ac);
	}

	TerminalSymbol getTerminal(String t, boolean retNull) {
		TerminalSymbol term = aSintatico.getTerminal(t);
		if (retNull)
			term.setAction(new TokenAction() {
			public Object act(Token tk) {
				return null;
			}
		});
		else
			term.setAction(new TokenAction() {
			public Object act(Token tk) {
				return tk.getValue();
			}
		});
		return term;
	}

	TerminalSymbol tChar = getTerminal("char", false);
	TerminalSymbol tAPar = getTerminal("(", true);
	TerminalSymbol tFPar = getTerminal(")", true);
	TerminalSymbol tACol = getTerminal("[", true);
	TerminalSymbol tFCol = getTerminal("]", true);
	TerminalSymbol tOR = getTerminal("|", true);
	TerminalSymbol tRange = getTerminal("-", true);
	TerminalSymbol tUmMais = getTerminal("+", true);
	TerminalSymbol tZeroMais = getTerminal("*", true);
	TerminalSymbol tZeroUm = getTerminal("?", true);

	NonTerminalSymbol nRegExp = aSintatico.getNonTerminal("regExp");
	NonTerminalSymbol nSeqExp = aSintatico.getNonTerminal("seqExp");
	NonTerminalSymbol nSingleExp = aSintatico.getNonTerminal("singleExp");
	NonTerminalSymbol nRanges = aSintatico.getNonTerminal("ranges");

	Parser regExpParser;

	char string2Char(String s) {
		char c0 = s.charAt(0);
		if (c0 != '\\')
			return c0;
		char c1 = s.charAt(1);
		if (c1 == 'n')
			return '\n';
		if (c1 == 'r')
			return '\r';
		if (c1 == 't')
			return '\t';
		return c1;
	}

	SemanticAction acDefault = new SemanticAction() {
		public Object act(Object args[]) {
			for (int c = 0; c < args.length; c++)
				if (args[c] != null)
					return args[c];
			return null;
		}
	};
	SemanticAction acSequence = new SemanticAction() {
		public Object act(Object args[]) {
			return RegExp.sequence((RegExp) args[0], (RegExp) args[1]);
		}
	};
	SemanticAction acChoice = new SemanticAction() {
		public Object act(Object args[]) {
			return RegExp.choice((RegExp) args[0], (RegExp) args[2]);
		}
	};
	SemanticAction acChar = new SemanticAction() {
		public Object act(Object args[]) {
			return RegExp.charExp(string2Char((String) args[0]));
		}
	};
	SemanticAction acZeroUm = new SemanticAction() {
		public Object act(Object args[]) {
			return RegExp.zeroOne((RegExp) args[0]);
		}
	};
	SemanticAction acUmMais = new SemanticAction() {
		public Object act(Object args[]) {
			return RegExp.oneMany((RegExp) args[0]);
		}
	};
	SemanticAction acZeroMais = new SemanticAction() {
		public Object act(Object args[]) {
			return RegExp.zeroMany((RegExp) args[0]);
		}
	};
        SemanticAction acWithRange = new SemanticAction() {
		public Object act(Object args[]) {
		    return RegExp.choice(RegExp.charExp('-'),(RegExp)args[2]);
		}
	};
	SemanticAction acRange = new SemanticAction() {
		public Object act(Object args[]) {
			RegExp resul =
				RegExp.charExp(
					string2Char((String) args[0]),
					string2Char((String) args[2]));
			if (args.length > 3)
				resul = RegExp.choice(resul, (RegExp) args[3]);
			return resul;
		}
	};

	SemanticAction acCharRange = new SemanticAction() {
		public Object act(Object args[]) {
			RegExp resul = RegExp.charExp(string2Char((String) args[0]));
			if (args.length > 1)
				resul = RegExp.choice(resul, (RegExp) args[1]);
			return resul;
		}
	};
	SemanticAction acEmptyRange = new SemanticAction() {
		public Object act(Object args[]) {
			return null;
		}
	};

	public RegExpCompiler() {

		addProduction(acDefault, nRegExp, new GrammarSymbol[] { nSeqExp });
		addProduction(
			acChoice,
			nRegExp,
			new GrammarSymbol[] { nSeqExp, tOR, nRegExp });
		addProduction(acDefault, nSeqExp, new GrammarSymbol[] { nSingleExp });
		addProduction(
			acSequence,
			nSeqExp,
			new GrammarSymbol[] { nSingleExp, nSeqExp });
		addProduction(acChar, nSingleExp, new GrammarSymbol[] { tChar });
		addProduction(
			acDefault,
			nSingleExp,
			new GrammarSymbol[] { tACol, nRanges, tFCol });
		addProduction(
			acWithRange,
			nSingleExp,
			new GrammarSymbol[] { tACol, tRange, nRanges, tFCol });

		addProduction(
			acDefault,
			nSingleExp,
			new GrammarSymbol[] { tAPar, nRegExp, tFPar });
		addProduction(
			acUmMais,
			nSingleExp,
			new GrammarSymbol[] { nSingleExp, tUmMais });
		addProduction(
			acZeroMais,
			nSingleExp,
			new GrammarSymbol[] { nSingleExp, tZeroMais });
		addProduction(
			acZeroUm,
			nSingleExp,
			new GrammarSymbol[] { nSingleExp, tZeroUm });

		addProduction(
			acCharRange,
			nRanges,
			new GrammarSymbol[] { tChar, nRanges });
		addProduction(
			acRange,
			nRanges,
			new GrammarSymbol[] { tChar, tRange, tChar, nRanges });
		addProduction(acCharRange, nRanges, new GrammarSymbol[] { tChar });
		addProduction(
			acRange,
			nRanges,
			new GrammarSymbol[] { tChar, tRange, tChar });

		RegExp charExp = RegExp.charExp('0', '9');
		charExp = RegExp.choice(charExp, RegExp.charExp('a', 'z'));
		charExp = RegExp.choice(charExp, RegExp.charExp('A', 'Z'));
		charExp = RegExp.choice(charExp, RegExp.string(" "));
		charExp = RegExp.choice(charExp, RegExp.string("\n"));
		charExp = RegExp.choice(charExp, RegExp.string("\\n"));
		charExp = RegExp.choice(charExp, RegExp.string("\r"));
		charExp = RegExp.choice(charExp, RegExp.string("\\r"));
		charExp = RegExp.choice(charExp, RegExp.string("\t"));
		charExp = RegExp.choice(charExp, RegExp.string("\\t"));
		charExp = RegExp.choice(charExp, RegExp.string("\\+"));
		charExp = RegExp.choice(charExp, RegExp.string("\\*"));
		charExp = RegExp.choice(charExp, RegExp.string("\\-"));
		charExp = RegExp.choice(charExp, RegExp.string("\\?"));
		charExp = RegExp.choice(charExp, RegExp.string("\\["));
		charExp = RegExp.choice(charExp, RegExp.string("\\]"));
		charExp = RegExp.choice(charExp, RegExp.string("{"));
		charExp = RegExp.choice(charExp, RegExp.string("}"));
		charExp = RegExp.choice(charExp, RegExp.string("\\("));
		charExp = RegExp.choice(charExp, RegExp.string("\\)"));
		charExp = RegExp.choice(charExp, RegExp.string("\\ "));
		charExp = RegExp.choice(charExp, RegExp.string("\\\\"));
		charExp = RegExp.choice(charExp, RegExp.string("/"));
		charExp = RegExp.choice(charExp, RegExp.string("<"));
		charExp = RegExp.choice(charExp, RegExp.string(">"));
		charExp = RegExp.choice(charExp, RegExp.string("\""));
		charExp = RegExp.choice(charExp, RegExp.string("&"));
		charExp = RegExp.choice(charExp, RegExp.string("%"));
		charExp = RegExp.choice(charExp, RegExp.string(";"));
		charExp = RegExp.choice(charExp, RegExp.string("="));
		charExp = RegExp.choice(charExp, RegExp.string(":"));
		charExp = RegExp.choice(charExp, RegExp.string(","));
		charExp = RegExp.choice(charExp, RegExp.string("."));
		charExp = RegExp.choice(charExp, RegExp.string("|"));
		charExp = RegExp.choice(charExp, RegExp.string("!"));
		charExp = RegExp.choice(charExp, RegExp.string("~"));
		charExp = RegExp.choice(charExp, RegExp.string("#"));
		charExp = RegExp.choice(charExp, RegExp.string("="));
		charExp = RegExp.choice(charExp, RegExp.string("_"));
		charExp = RegExp.choice(charExp, RegExp.string("\""));
		charExp = RegExp.choice(charExp, RegExp.string("'"));

		//aLexico.insertBlank(RegExp.string(" "));
		aLexico.insertRule(charExp, tChar);
		aLexico.insertRule(RegExp.string("("), tAPar);
		aLexico.insertRule(RegExp.string(")"), tFPar);
		aLexico.insertRule(RegExp.string("["), tACol);
		aLexico.insertRule(RegExp.string("]"), tFCol);
		aLexico.insertRule(RegExp.string("|"), tOR);
		aLexico.insertRule(RegExp.string("-"), tRange);
		aLexico.insertRule(RegExp.string("+"), tUmMais);
		aLexico.insertRule(RegExp.string("*"), tZeroMais);
		aLexico.insertRule(RegExp.string("?"), tZeroUm);

		aSintatico.startSymbol(nRegExp);
		regExpParser = new Parser(aSintatico, true);

		//regExpParser.printTable();

	}

	synchronized RegExp compile(String source)
		throws /*LexicalErrorException, ParsingException*/ CompilerException {
		Token tokens[] = aLexico.analyse(source);
		
		RegExp resul =
			(RegExp) regExpParser.parse(
				tokens,
				Parser.BACKTRACKING_PARSING,
				true);

		
				
		return resul;

	}

	public static void main(String args[]) throws Exception {
		RegExpCompiler comp = new RegExpCompiler();

		System.out.println((int) '0');
		System.out.println((int) 'z');
		System.out.println((int) '[');

		System.out.println(comp.compile("<spec name=[a-z]+"));
		//comp.regExpParser.saveGrammarLog();
		comp.regExpParser.saveParserLog();
	}

}
